/*
    Tray Timer, a system tray stopwatch/timer
    Copyright (C) 2009  Pratik J Bhatt
	http://traytimer.codeplex.com/

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version. Refer to LICENSE.txt for the full
	text of the license.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "Application.h"

Application::Application(HINSTANCE hInstance, int nCmdShow)
{
	HRESULT hr;

	iTime = 0;
	mouseMove = false;
	settings = new AppSettings();	
	
	trayMenu = new PopupMenu();
	
	this->hInst = hInstance;
	hr = initWindows(nCmdShow);
	formatDisplayString();
	timerAlarm = new Alarm(false, IDC_MINUTES, IDC_MINUTES);

}
Application::Application(void)
{
	iTime = 0;
}

Application::~Application(void)
{
	delete trayMenu;
	delete trayIcon;
	delete activityTimer;
	delete displayTimer;
	delete settings;
	delete timerAlarm;
}

HRESULT Application::initWindows(int nCmdShow)
{
	HRESULT hr;
	RECT rcDesktopWindow, rcMainWindow;

	hr = S_OK;
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
	wcex.lpfnWndProc	= WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInst;
	wcex.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	=  CreateSolidBrush(RGB(252, 89, 0));
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= L"Application";
	wcex.hIconSm		= LoadIcon(NULL, IDI_APPLICATION);

	RegisterClassEx(&wcex);

	hWnd = CreateWindowEx(WS_EX_TOOLWINDOW, L"Application", L"Time Tracker v1.0", WS_POPUPWINDOW, 
						CW_USEDEFAULT, CW_USEDEFAULT, 200, 90, NULL, NULL, hInst, NULL);

	if (!hWnd)
	{
		MessageBox(NULL, L"Failed to get Handle", L"Failed", MB_OK);
		PostQuitMessage(-10);
	}

	SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktopWindow, NULL);
	GetWindowRect(hWnd, &rcMainWindow);
	SetWindowPos(hWnd, HWND_TOPMOST, rcDesktopWindow.right - (rcMainWindow.right - rcMainWindow.left)- 10 , 
					rcDesktopWindow.bottom - (rcMainWindow.bottom - rcMainWindow.top) - 10, 0, 0, SWP_NOSIZE);

	trayIcon = new SystemTrayIcon(hInst, hWnd, WM_SYS_TRAY_ICON_MSG);
	activityTimer = new Timer(hWnd, 60000, IDT_ACT_TIMER);
	displayTimer = new Timer(hWnd, 5000, IDT_DIS_TIMER);

	ShowWindow(hWnd, nCmdShow);

	return hr;
}

int Application::run()
{
	MSG msg;

	msg.message = WM_NULL;

	while(GetMessage(&msg, NULL, 0, 0))
	{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
	}

	return (int) msg.wParam;
}

LRESULT Application::msgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hDc;
	PAINTSTRUCT ps;
	RECT rc;
	RECT rcTextRect, rcDesktopWindow, rcMainWindow;
	HFONT hFont;
	LOGFONT logicalFont;

	memset(&rcTextRect, 0, sizeof(RECT));
	GetClientRect(hWnd, &rc);

	switch(message)
	{
	case WM_CREATE:
		break;

	case WM_CLOSE:
		ShowWindow(hWnd, SW_HIDE);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	case WM_PAINT:
		hDc = BeginPaint(hWnd, &ps);
		{
			SetBkMode(hDc, TRANSPARENT);
			SetTextColor(hDc, RGB(255,255,255));

			hFont = utilities::getSimpleFont(hDc, L"Verdana",  SF_NORMAL, 65);

			GetObject(hFont, sizeof(LOGFONT), &logicalFont);
			SelectObject(hDc, hFont);		
			DrawText(hDc, displayString, -1, &rcTextRect, DT_CALCRECT);

			if ( (rcTextRect.right - rcTextRect.left) > (rc.right - rc.left))
			{
				SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktopWindow, NULL);
				GetWindowRect(hWnd, &rcMainWindow);
				int leftpos = rcDesktopWindow.right - (rcTextRect.right - rcTextRect.left) - 30;
				SetWindowPos(hWnd, HWND_TOPMOST, leftpos , 
					rcDesktopWindow.bottom - (rcMainWindow.bottom - rcMainWindow.top) - 10, (rcTextRect.right - rcTextRect.left) + 20, 
					rcMainWindow.bottom - rcMainWindow.top, NULL);
				InvalidateRect(hWnd, &rc, TRUE);
				break;
			}

			DrawText(hDc, displayString, -1, &rc, DT_VCENTER | DT_CENTER);

			SetBkMode(hDc, OPAQUE);
			SetTextColor(hDc, RGB(0,0,0));
			DeleteObject(SelectObject(hDc, GetStockObject(SYSTEM_FONT)));
			DeleteObject(hFont);
		}
		EndPaint(hWnd, &ps);
		break;

	case WM_COMMAND:
		switch(LOWORD(wParam))
		{
		case ID_START:
			activityTimer->start();
			trayMenu->setStarted(true);
			trayIcon->modifyIconStatus(ICS_RUNNING);
			break;

		case ID_PAUSE:
			activityTimer->pause();
			trayMenu->setPaused(true);
			trayIcon->modifyIconStatus(ICS_PAUSED);
			break;

		case ID_RESUME:
			activityTimer->resume();
			trayMenu->setPaused(false);
			trayIcon->modifyIconStatus(ICS_RUNNING);
			break;

		case ID_STOP:
			activityTimer->stop();
			trayMenu->setStarted(false);
			trayIcon->modifyIconStatus(ICS_STOPPED);
			break;
		case ID_DISPLAY:
			trayMenu->setTimerShown(true);
			ShowWindow(hWnd, SW_SHOW);
			break;

		case ID_HIDE:
			ShowWindow(hWnd, SW_HIDE);
			trayMenu->setTimerShown(false);
			break;

		case ID_RESET:
			iTime = 0;
			formatDisplayString();
			trayIcon->modifyIconTip(L"Time Tracker");
			InvalidateRect(hWnd, &rc, TRUE);
			break;

		case ID_SETTINGS:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_SETTINGS), hWnd, Settings);
			if (settings->changed)
				SendMessage(hWnd, WM_SETTINGS_CHANGED, 0, 0); 
			break;

		case ID_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); 
			break;

		case ID_EXIT:
			if (activityTimer->isRunning())
			{
				int confirm = MessageBox(NULL, L"Timer is still active!!\n Do you really want to Quit?", 
										 L"Confirm", MB_ICONQUESTION | MB_YESNO);
				if (confirm == IDNO)
					break;
			}
			DestroyWindow(hWnd);
			PostQuitMessage(0);
			break;
		};
		break;

	case WM_SYS_TRAY_ICON_MSG:
		switch(lParam)
		{
		case WM_LBUTTONUP:
			displayTimer->start();
			PostMessage(hWnd, WM_COMMAND, ID_DISPLAY, 0);
			break;

		case WM_RBUTTONUP:
			trayMenu->display(hWnd);
			break;
		};
		break;

	case WM_TIMER:
		switch (wParam)
		{
		case IDT_ACT_TIMER:
			if(!activityTimer->isPaused())
			{
				iTime++;
				if (timerAlarm->isOn() && timerAlarm->ringAlarm(iTime, settings->precision))
					SendMessage(hWnd, WM_ALARM, NULL, NULL);
			}
			formatDisplayString();
			trayIcon->modifyIconTip(displayString);
			InvalidateRect(hWnd, &rc, TRUE);
			break;

		case IDT_DIS_TIMER:
			displayTimer->stop();
			PostMessage(hWnd, WM_COMMAND, ID_HIDE, 0);
			break;
		};
		break;

	case WM_RBUTTONUP:
		trayMenu->display(hWnd);
		break;

	case WM_SETTINGS_CHANGED:
		if(settings->precisionChanged)
			changeTimer();

		if (settings->alarmChanged)
			changeAlarm();

		if (settings->autoRunChanged)
			changeAutorun();

		settings->changed = false;

		InvalidateRect(hWnd, &rc, TRUE);
		break;

	case WM_LBUTTONDBLCLK:
		SendMessage(hWnd, WM_COMMAND, (WPARAM)ID_HIDE, NULL);
		break;

	case WM_LBUTTONDOWN:
		mouseMove = true;
		SetCapture(hWnd);
		break;

	case WM_MOUSEMOVE:
		if(mouseMove)
		{
			RECT mainWindowRect;
			POINT cursorPos, newWindowPos;
			int windowWidth, windowHeight;
			
			cursorPos.x = (int)(short)LOWORD(lParam); //Implementing the GET_X_LPARAM to avoid windowsx.h include
			cursorPos.y = (int)(short)HIWORD(lParam); //Implementing the GET_Y_LPARAM to avoid windowsx.h include
			
			GetWindowRect(hWnd, &mainWindowRect);
			windowWidth = mainWindowRect.right - mainWindowRect.left;
			windowHeight = mainWindowRect.bottom - mainWindowRect.top;

			ClientToScreen(hWnd, &cursorPos);
			newWindowPos.x = cursorPos.x - (windowWidth) / 2;
			newWindowPos.y = cursorPos.y - (windowHeight) / 2;
			MoveWindow(hWnd, newWindowPos.x, newWindowPos.y, windowWidth , windowHeight, TRUE);
		}
		break;
	case WM_LBUTTONUP:
		ReleaseCapture();
		mouseMove = false;
		break;

	case WM_ALARM:
		displayTimer->start();
		PostMessage(hWnd, WM_COMMAND, ID_DISPLAY, 0);
		PlaySound((LPCWSTR)SND_ALIAS_SYSTEMEXCLAMATION, NULL, SND_ALIAS_ID | SND_ASYNC);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	};
	
	return 0;
}

INT_PTR Application::AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	RECT rcDesktop, rcDlg;

	switch (message)
	{
	case WM_INITDIALOG:
		SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktop, NULL);
		GetWindowRect(hDlg, &rcDlg);
		SetWindowPos(hDlg, HWND_TOP, rcDesktop.right - (rcDlg.right - rcDlg.left)- 10 , 
					 rcDesktop.bottom - (rcDlg.bottom - rcDlg.top) - 10, 0, 0, SWP_NOSIZE);
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

void Application::formatDisplayString()
{
	wchar_t tempString[256];
	long int tempHours, tempMinutes;

	tempMinutes = 0;
	tempHours = 0;

	if (settings->precision == IDC_HOURS)
	{
		tempHours = iTime / 24;
		tempMinutes = iTime % 24;
	}
	else
	{
		tempHours = iTime / 60;
		tempMinutes = iTime % 60;
	}
	
	StringCbPrintf(tempString, 256, L"%d:%02d", tempHours, tempMinutes);

	wcscpy_s(displayString, tempString);
}

INT_PTR Application::SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	RECT rcDesktop, rcDlg;

	static AppSettings *prevSettings;

	switch (message)
	{
	case WM_INITDIALOG:
		SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktop, NULL);
		GetWindowRect(hDlg, &rcDlg);
		SetWindowPos(hDlg, HWND_TOP, rcDesktop.right - (rcDlg.right - rcDlg.left)- 10 , 
					 rcDesktop.bottom - (rcDlg.bottom - rcDlg.top) - 10, 0, 0, SWP_NOSIZE);
		CheckDlgButton(hDlg, settings->precision, BST_CHECKED);

		if(settings->alarmActive)
		{
			CheckDlgButton(hDlg, IDC_ALARM_ON, BST_CHECKED);
			EnableWindow(GetDlgItem(hDlg, IDC_ALARM_INT), TRUE);
			EnableWindow(GetDlgItem(hDlg, IDC_ALARM_PREC), TRUE);
			SetDlgItemInt(hDlg, IDC_ALARM_INT, settings->alarmIntervel, FALSE);
		}
		else
			CheckDlgButton(hDlg, IDC_ALARM_OFF, BST_CHECKED);

		if(settings->autoRun)
			CheckDlgButton(hDlg, IDC_AUTORUN, BST_CHECKED);
		

		prevSettings = new AppSettings();
		*prevSettings = *settings;

		populateComboBox(hDlg, settings->precision);
		int currentSelected;
		currentSelected = 0;
		switch (settings->alarmPrecision)
		{
		case IDC_MINUTES:
			if (settings->precision == IDC_SECONDS)
				currentSelected = 1;
			break;

		case IDC_HOURS:
			switch (settings->precision)
			{
			case IDC_SECONDS:
				currentSelected = 2;
				break;
			case IDC_MINUTES:
				currentSelected = 1;
				break;
			};
		};

		SendMessage(GetDlgItem(hDlg, IDC_ALARM_PREC), CB_SETCURSEL, (WPARAM) currentSelected, 0);
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_OK:
			{	
				BOOL success;
				prevSettings->alarmIntervel = GetDlgItemInt(hDlg, IDC_ALARM_INT, &success, FALSE);
				if (!success)
				{
					prevSettings->alarmIntervel = 0;
				}
			}
			if (prevSettings->precisionChanged)
			{
				int confirm;
				confirm = MessageBox(NULL, L"This will reset the timer!!\n Are you sure?", L"Confirm", MB_YESNO | MB_ICONEXCLAMATION);
				if (confirm == IDNO)
					break;
				
			}
			*settings = *prevSettings;
			delete prevSettings;
			EndDialog(hDlg, LOWORD(wParam));
			break;

		case IDC_CANCEL:
			delete prevSettings;
			EndDialog(hDlg, LOWORD(wParam));
			break;

		case IDC_SECONDS:
		case IDC_MINUTES:
		case IDC_HOURS:
			if(settings->precision != LOWORD(wParam))
			{
				prevSettings->precision = LOWORD(wParam);
				prevSettings->precisionChanged = true;
				prevSettings->changed = true;
			}
			else
				prevSettings->precisionChanged = false;

			populateComboBox(hDlg, LOWORD(wParam));
			break;

		case IDC_ALARM_ON:
			prevSettings->alarmActive = true;
			if (settings->alarmActive != prevSettings->alarmActive)
			{
				prevSettings->alarmChanged = true;
				prevSettings->changed = true;
			}
			else
				prevSettings->alarmChanged = false;

			EnableWindow(GetDlgItem(hDlg, IDC_ALARM_INT), TRUE);
			EnableWindow(GetDlgItem(hDlg, IDC_ALARM_PREC), TRUE);
			break;

		case IDC_ALARM_OFF:
			prevSettings->alarmActive = false;
			if (settings->alarmActive != prevSettings->alarmActive)
			{
				prevSettings->alarmChanged = true;
				prevSettings->changed = true;
			}
			else
				prevSettings->alarmChanged = false;

			EnableWindow(GetDlgItem(hDlg, IDC_ALARM_INT), FALSE);
			EnableWindow(GetDlgItem(hDlg, IDC_ALARM_PREC), FALSE);
			break;

		case IDC_ALARM_PREC:
			switch (HIWORD(wParam))
			{
			case CBN_SELCHANGE:
				int selectedItemIndex;
				selectedItemIndex = (int) SendMessage((HWND) lParam, CB_GETCURSEL, NULL, NULL); 
				switch (selectedItemIndex)
				{
				case 0:
					switch (prevSettings->precision)
					{
					case IDC_SECONDS:
						prevSettings->alarmPrecision = IDC_SECONDS;
						break;

					case IDC_MINUTES:
						prevSettings->alarmPrecision = IDC_MINUTES;
						break;

					case IDC_HOURS:
						prevSettings->alarmPrecision = IDC_HOURS;
						break;
					};
					break;

				case 1:
					switch (prevSettings->precision)
					{
					case IDC_SECONDS:
						prevSettings->alarmPrecision = IDC_MINUTES;
						break;
					case IDC_MINUTES:
						prevSettings->alarmPrecision = IDC_HOURS;
						break;
					};
					break;

				case 2:
					prevSettings->alarmPrecision = IDC_HOURS;
					break;
				};
				if (settings->alarmPrecision != prevSettings->alarmPrecision)
					prevSettings->alarmChanged = true;
				break;
			};
			break;
		case IDC_AUTORUN:
			if(prevSettings->autoRun)
				prevSettings->autoRun = false;
			else
				prevSettings->autoRun = true;

			if(settings->autoRun != prevSettings->autoRun)
			{
				prevSettings->autoRunChanged = true;
				prevSettings->changed = true;
			}
			else
				prevSettings->autoRunChanged = false;

			break;
		};
		break;

	default:
		return (INT_PTR) FALSE;
	};
	return (INT_PTR) TRUE;
}

void Application::populateComboBox(HWND hDlg, UINT precisionSelected)
{
	HWND comboBox = GetDlgItem(hDlg, IDC_ALARM_PREC);
	SendMessage(comboBox, CB_RESETCONTENT, NULL, NULL);

	switch (precisionSelected)
	{
	case IDC_SECONDS:
		SendMessage(comboBox, CB_ADDSTRING, NULL,(LPARAM) L"Seconds");
		//Fall through
	case IDC_MINUTES:
		SendMessage(comboBox, CB_ADDSTRING, NULL,(LPARAM) L"Minutes");
		//Fall through
	case IDC_HOURS:
		SendMessage(comboBox, CB_ADDSTRING, NULL,(LPARAM) L"Hours");
		break;
	};
}

void inline Application::changeTimer()
{
		settings->precisionChanged = false;
		
		activityTimer->stop();
		delete activityTimer;
		UINT tempIntervel;
		switch (settings->precision)
		{
		case IDC_SECONDS:
			 tempIntervel = 1000;
			 break;
		case IDC_MINUTES:
			tempIntervel = 60000;
			break;
		case IDC_HOURS:
			tempIntervel = 3600000;
			break;
		};
		activityTimer = new Timer(hWnd, tempIntervel, IDT_ACT_TIMER);
		iTime = 0;
		formatDisplayString();
}

void inline Application::changeAlarm()
{
	if (timerAlarm != NULL)
		delete timerAlarm;

	timerAlarm = new Alarm(settings->alarmActive, settings->alarmIntervel, settings->alarmPrecision);
	
}

void inline Application::changeAutorun()
{
	wchar_t buffer[256];
	wchar_t filePath[256];
		
	DWORD ret;

	ret = GetEnvironmentVariable(L"USERPROFILE", buffer, sizeof(buffer));		

	wcscpy_s(filePath, L"\\?\\");
	wcscat_s(filePath, buffer);
	
	MessageBox(NULL, buffer, L"TEST", MB_OK);

	wcscat_s(filePath, L"\\Start Menu\\Programs\\Startup\\Time Tracker.lnk");	

	MessageBox(NULL, filePath, L"Test", MB_OK);

	if(settings->autoRun)
	{
		int res = CopyFile(L"\\?\\Copy of Time Tracker.exe", filePath, FALSE);

		if (res == 0)
		{
			DWORD err = GetLastError();

			if (err == ERROR_FILE_NOT_FOUND)
			MessageBox(NULL, L"Src Not Found", L"Error", MB_OK);
			else
				MessageBox(NULL, L"Fail", L"Error", MB_OK);
		}
	}
}